package no.sachmuld.methodcombination.api.adapter;

import no.sachmuld.methodcombination.api.*;
import no.sachmuld.methodcombination.impl.AbstractMethodInvocation;
import no.sachmuld.methodcombination.impl.MultiPostcondition;
import no.sachmuld.methodcombination.impl.MultiPrecondition;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public abstract class DesignByContract<B, R> {

    private static Logger logger = Logger.getLogger(DesignByContract.class);

    private BehaviourChain<B> chain;

    public DesignByContract(BehaviourChain<B> chain) {
        this.chain = chain;
    }

    public R execute() {

        List<Condition> preconditions = new ArrayList<>();
        for (B behaviour : chain.bottomUp()) {
            preconditions.add(executePrecondition(behaviour));
        }
        MultiPrecondition multiPrecondition = new MultiPrecondition(preconditions);
        logger.info(multiPrecondition);
        assert multiPrecondition.getValue() : multiPrecondition;

        R result = new AbstractMethodInvocation<B, R>(chain.bottomUp(), null, null, null) {
            @Override
            protected R executeBehaviour(B behaviour) {
                return executePrimary(behaviour, this);
            }
        }.execute();

        List<Condition> postconditions = new ArrayList<>();
        for (B behaviour : chain.bottomUp()) {
            postconditions.add(executePostcondition(behaviour, result));
        }
        MultiPostcondition multiPostcondition = new MultiPostcondition(postconditions);
        logger.info(multiPostcondition);
        assert multiPostcondition.getValue() : multiPostcondition;

        return result;
    }

    protected abstract Condition executePrecondition(B behaviour);
    protected abstract R executePrimary(B behaviour, MethodInvocation<R> methodInvocation);
    protected abstract Condition executePostcondition(B behaviour, R result);

}
